let walk = require('./walk');
let Scope = require('./scope');
let {has} = require('../utils');

function analyse(ast, magicStringOfAst, module) {
  let scope = new Scope();
  ast.body.forEach(statement => {
    function addToScope(name, isBlockDeclaration = false) {
      const realScope = scope.add(name, isBlockDeclaration);
      if(!realScope.parent) { // 如果此作用域，没有父作用域,那这就是顶级变量，根作用域下的变量，可以放在 _defines = true;
        // 模块内的顶级变量
        statement._defines[name] = true;
      }
    }

    Object.defineProperties(statement, {
      _module: {value:module},// 当前的模块
      _defines: {value: {}},// 当前 statement 语法树节点声明了哪些变量
      _modifies: {value: {}}, // 当前 statement 包含的修改语句
      _dependsOn: {value: {}}, // 当前 statement 语句外部依赖的变量
      _included: {value: false, writable: true}, // 当前的语句是否放置在结果中， 是否会出现在打包结果中
      _source: { value: magicStringOfAst.snip(statement.start, statement.end)} // key 是 _source 值是这个语法树节点在源码中的源代码
    });
    // 如何知道某个变量有没有在当前模块内定义呢？
    // 原理是这样 扫描整个模块，找到所有定义的变量
    // 构建作用域链
    walk(statement, ast.body, {
      enter(node) {
        let newScope;
        switch(node.type) {
          case 'FunctionDeclaration':
            // 函数的参数
            const names = node.params.map(param => param.name);
            addToScope(node.id.name); // 把 node 也就是 say 这个变量，添加到当前作用域内
            // 如果遇到函数声明，就会产生一个新的作用域
            newScope = new Scope({name:node.id.name ,parent: scope, params: names, isBlock: false});
            break;
          case 'BlockStatement':
            newScope = new Scope({name:node.type ,parent: scope, params: [], isBlock: true});
            break;
          case 'VariableDeclaration':
            node.declarations.forEach(declaration => {
              if(node.kind === 'let' || node.kind === 'const') {
                addToScope(declaration.id.name, true);
              } else if(node.kind === 'var'){
                addToScope(declaration.id.name);
              }
            });
            break;
        }
        if(newScope) { // 如果创建了新的作用域，那么这个作用域将会成为新的当前作用域
          Object.defineProperty(node, '_scope', {value: newScope});
          scope = newScope;
        }
      },
      leave(node) {
        //当离开节点的时候 ，如果发现这个节点创建 新的作用域，就回退到使用域
        if (has(node,'_scope')) {
            scope = scope.parent;
        }
    }
    });
  });

  // 作用域链构建完后，再遍历一次，找出本模块依赖了哪些变量
  ast.body.forEach(statement => {
    function checkForReads(node) {
      if(node.type === 'Identifier') {
        // let currentScope = node._scope || scope;
        // 查找是否有在本模块下定义，如果找不到证明就是外部引入的
        // let definingScope = currentScope.findDefiningScope(node.name);
        // 如果这个变量不在当前模块声明才会放到 _dependsOn
        // if(!definingScope) {
          // 找这个 statement 依赖了哪些外部变量
          statement._dependsOn[node.name] = true;
        // }
      }
    }
    function checkForWrites(node) {
      if(node.type === 'AssignmentExpression') {
        // node 其实就是被赋值的变量名
        function addNode(node) {
          statement._modifies[node.name] = true;
        }
        addNode(node.left,true);
      } else if (node.type === 'UpdateExpression') {
        addNode(node.argument);
      }
    }
    walk(statement, ast.body, {
      enter(node) {
        if(has(node, '_scope')) {
          scope = node._scope;
        }
        // 检查当前节点读取了哪些变量
        checkForReads(node);
        // 检查当前节点修改了哪些变量
        checkForWrites(node);
        
      },
      leave(node) {
        if(has(node, '_scope')) {
          scope = scope.parent;
        }
      }
    });
  });
  
}

module.exports = analyse;